/**
 * File:		Section.java
 * Project:		Virtual Advisor
 * Module:		Client App
 * Author:		Rotem Ganel
 * Created:		May 1, 2011
 * Updated:		May 1, 2011
 * Version:		1 (in progress)
 */

package clientApp;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Section implements Comparable<Section>, Serializable {
	private static final long serialVersionUID = -3054035646158791613L;

	public static enum REQ_TYPE {Take_All, Take_Any, Take_X_Creds, Take_X_Courses, XOR, INVALID};
	
	public char identifier;
	public String name;
	private ArrayList<Subsection> subsections;
	private REQ_TYPE reqType;
	private int unitsNeeded;
		
	public Section(char identifier, String name) {
		this.identifier = identifier;
		this.name = name;
		this.subsections = new ArrayList<Subsection>();
		reqType = REQ_TYPE.Take_All;
		unitsNeeded = -1;
	}

	public Section(char identifier, String name, REQ_TYPE reqType) {
		this.identifier = identifier;
		this.name = name;
		this.subsections = new ArrayList<Subsection>();
		
		if ((reqType == REQ_TYPE.Take_X_Courses) || (reqType == REQ_TYPE.Take_X_Creds)) {
			reqType = REQ_TYPE.Take_All;
		} else {
			this.reqType = reqType;
		}

		unitsNeeded = -1;
	}

	public Section(char identifier, String name, REQ_TYPE reqType, int unitsNeeded) {
		this.identifier = identifier;
		this.name = name;
		this.subsections = new ArrayList<Subsection>();
		
		if (((reqType == REQ_TYPE.Take_X_Courses) || (reqType == REQ_TYPE.Take_X_Creds)) && (unitsNeeded < 1)) {
			reqType = REQ_TYPE.Take_All;
			this.unitsNeeded = -1;
		} else {
			this.reqType = reqType;
			this.unitsNeeded = unitsNeeded;
		}
	}
	
	public Section(Section other) {
		this(other.identifier, other.name, other.reqType, other.unitsNeeded);
		
		for (Subsection s : other.subsections) {
			this.subsections.add(new Subsection(s));
		}
	}

	public String[] getAllCourses() {
		ArrayList<String> courseList = new ArrayList<String>();
		
		for (Subsection s : subsections) {
			String[][] courses = s.getCourses();
			for (String[] series : courses) {
				for (String course : series) {
					courseList.add(course);
					
					if (s.getHonors(course)) {
						courseList.add(course + "H");
					}
				}
			}
			
		}
		
		Collections.sort(courseList);
		return toArray(courseList);
		
	}
	
	public REQ_TYPE getReqType() {
		return reqType;
	}
	
	public boolean setReqType(REQ_TYPE reqType) {
		if (((reqType == REQ_TYPE.Take_X_Courses) || (reqType == REQ_TYPE.Take_X_Creds)) && (unitsNeeded < 1)) {
			return false;
		} else {
			this.reqType = reqType;
			return true;
		}
	}
	
	public int getUnitsNeeded() {
		return unitsNeeded;
	}
	
	public boolean setUnitsNeeded(int unitsNeeded) {
/*		if (((reqType == REQ_TYPE.Take_X_Courses) || (reqType == REQ_TYPE.Take_X_Creds)) && (unitsNeeded > 0)) {
			this.unitsNeeded = unitsNeeded;
			return true;
		} else {
			return false;
		}*/
		
		this.unitsNeeded = unitsNeeded;
		return true;
	}
	
	public int numSubsections() {
		return subsections.size();
	}
	
	public REQ_TYPE getReqTypeBySubsection(int subsectionIdentifier) {
		if (subsectionIdentifier > subsections.size()) {
			return null;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.getReqType();
	}
	
	public int getUnitsNeededBySubsection(int subsectionIdentifier) {
		if (subsectionIdentifier > subsections.size()) {
			return -1;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.getUnitsNeeded();
	}

	public int addSubsection(REQ_TYPE reqType) {
		if (subsections.add(new Subsection(reqType, subsections.size() + 1))) {
			return subsections.size();
		} else {
			return -1;
		}
	}
	
	public int addSubsection(REQ_TYPE reqType, int unitsNeeded) {
		if (subsections.add(new Subsection(reqType, unitsNeeded, subsections.size() + 1))) {
			return subsections.size();
		} else {
			return -1;
		}
	}

	public boolean removeSubsection(int identifier) {
		if (identifier > subsections.size()) {
			return false;
		}
		
		subsections.remove(identifier - 1);
		
		for (int i = identifier - 1; i < subsections.size(); i++) {
			subsections.get(i).decrementIdentifier();
		}
		
		return true;
	}
	
	public String[][] getCoursesInSubsection(int subsectionIdentifier) {
		if (subsectionIdentifier > subsections.size()) {
			return null;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.getCourses();
	}

	public boolean addNewReqSeriesToSubsection(int subsectionIdentifier, String[] courseIDs) {
		if (subsectionIdentifier > subsections.size()) {
			return false;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.addReqSeries(courseIDs);
	}
	
	public boolean addNewReqSeriesToSubsection(int subsectionIdentifier, String[] courseIDs, char[] gradesNeeded) {
		if (subsectionIdentifier > subsections.size()) {
			return false;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.addReqSeries(courseIDs, gradesNeeded);
	}
	
	public boolean addNewReqSeriesToSubsection(int subsectionIdentifier, String[] courseIDs, boolean[] honors) {
		if (subsectionIdentifier > subsections.size()) {
			return false;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.addReqSeries(courseIDs, honors);
	}
	
	public boolean addNewReqSeriesToSubsection(int subsectionIdentifier, String[] courseIDs, char[] gradesNeeded, boolean[] honors) {
		if (subsectionIdentifier > subsections.size()) {
			return false;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.addReqSeries(courseIDs, gradesNeeded, honors);
	}

	public boolean removeReqSeriesFromSubsection(int subsectionIdentifier, String[] courseIDs) {
		if (subsectionIdentifier > subsections.size()) {
			return false;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.removeReqSeries(courseIDs);
	}
	
	public boolean addCourseToSubsection(int subsectionIdentifier, String[] reqSeries, String newCourseID) {
		if (subsectionIdentifier > subsections.size()) {
			return false;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.addCourseToSeries(reqSeries, newCourseID);
	}
	
	public boolean addCourseToSubsection(int subsectionIdentifier, String[] reqSeries, String newCourseID, char gradeNeeded) {
		if (subsectionIdentifier > subsections.size()) {
			return false;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.addCourseToSeries(reqSeries, newCourseID, gradeNeeded);
	}

	public boolean addCourseToSubsection(int subsectionIdentifier, String[] reqSeries, String newCourseID, boolean honors) {
		if (subsectionIdentifier > subsections.size()) {
			return false;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.addCourseToSeries(reqSeries, newCourseID, honors);
	}
	
	public boolean addCourseToSubsection(int subsectionIdentifier, String[] reqSeries, String newCourseID, char gradeNeeded, boolean honors) {
		if (subsectionIdentifier > subsections.size()) {
			return false;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.addCourseToSeries(reqSeries, newCourseID, gradeNeeded, honors);
	}
	
	public char getGradeNeeded(String courseID) {		
		for (Subsection s : subsections) {
			if (s.contains(courseID)) {
				return s.getGradeNeeded(courseID);
			}
		}

		return 'X';
	}
	
	public boolean getHonors(String courseID) {
		for (Subsection s : subsections) {
			if (s.contains(courseID)) {
				return s.getHonors(courseID);
			}
		}

		return false;
	}
	
	public boolean contains(String courseID) {
		for (Subsection s : subsections) {
			if (s.contains(courseID)) {
				return true;
			}
		}
		
		return false;
	}
	
	public boolean removeCourse(String removeCourseID) {
		for (Subsection s : subsections) {
			if (s.contains(removeCourseID)) {
				return s.removeCourse(removeCourseID);
			}
		}
		
		return false;
	}
	
	public boolean removeCourseFromSubsection(int subsectionIdentifier, String removeCourseID) {
		if (subsectionIdentifier > subsections.size()) {
			return false;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		
		if (s.contains(removeCourseID)) {
			return s.removeCourse(removeCourseID);
		} else {
			return false;
		}
	}
	
	//TODO: check all identifiers > 0
	public boolean removeCourseFromSubsection(int subsectionIdentifier, String[] reqSeries, String removeCourseID) {
		if (subsectionIdentifier > subsections.size()) {
			return false;
		}
		
		Subsection s = subsections.get(subsectionIdentifier - 1);
		return s.removeCourseFromSeries(reqSeries, removeCourseID);
	}
	
	public int compareTo(Section other) {
		if (identifier > other.identifier) {
			return 1;
		} else if (identifier == other.identifier) {
			return 0;
		} else {
			return -1;
		}
	}
	
	private static String[] toArray(ArrayList<String> arrayList) {
		String[] array = new String[arrayList.size()];
		for (int i = 0; i < array.length; i++) {
			array[i] = arrayList.get(i);
		}
		
		return array;
	}


	public static void main(String args[]) {
		ArrayList<Section> sections = new ArrayList<Section>();
		
		Section section = new Section('A', "Required Computer Science Classes");
		section.addSubsection(REQ_TYPE.Take_All);
		
		String[] courses = {"CMSC 201", "CMSC 202", "CMSC 203", "CMSC 304", "CMSC 313", "CMSC 331", "CMSC 341"};
		char[] gradeNeeded = {'B', 'B', 'C', 'C', 'C', 'C', 'C'};
		boolean[] honors = {true, true, false, false, false, false, true};
		section.addNewReqSeriesToSubsection(1, courses, gradeNeeded, honors);
		
		sections.add(section);
		
		Section math = new Section('B', "Required Mathematics Courses");
		math.addSubsection(REQ_TYPE.Take_All);
		String[] mathCourses = {"MATH 151", "MATH 152", "MATH 221"};
		char[] mathGrades = {'C', 'C', 'C'};
		math.addNewReqSeriesToSubsection(1, mathCourses, mathGrades);
		
		sections.add(math);
		
		Section stats = new Section('C', "Required Statistics Course");
		stats.addSubsection(REQ_TYPE.XOR);
		String[] statCourse1 = {"STAT 355"};
		char[] statGrade1 = {'C'};
		stats.addNewReqSeriesToSubsection(1, statCourse1, statGrade1);
		String[] statCourse2 = {"STAT 451"};
		char[] statGrade2 = {'C'};
		stats.addNewReqSeriesToSubsection(1, statCourse2, statGrade2);

		sections.add(stats);
		
		Section sci = new Section('D', "Required Science Courses", REQ_TYPE.Take_X_Creds, 12);
		sci.addSubsection(REQ_TYPE.XOR);
		String[] opt1 = {"BIOL 100", "BIOL 301"};
		sci.addNewReqSeriesToSubsection(1, opt1);
		String[] opt2 = {"BIOL 141", "BIOL 142"};
		sci.addNewReqSeriesToSubsection(1, opt2);
		String[] opt3 = {"CHEM 101", "CHEM 102"};
		sci.addNewReqSeriesToSubsection(1, opt3);
		String[] opt4 = {"PHYS 121", "PHYS 122"};
		sci.addNewReqSeriesToSubsection(1, opt4);
		sci.addSubsection(REQ_TYPE.Take_Any);
		String[] sci2List = {"BIOL 100L", "BIOL 251", "BIOL 251L", "BIOL 252", "BIOL 252L"};
		sci.addNewReqSeriesToSubsection(2, sci2List);

		sections.add(sci);
				
		for (Section s : sections) {
			System.out.print(s.identifier + ". " + s.name + ": ");
			
			REQ_TYPE secReqType = s.getReqType();
			String strSecType;
			if (secReqType == REQ_TYPE.Take_All) {
				strSecType = "Take All";
			} else if (secReqType == REQ_TYPE.Take_X_Courses) {
				int unitsNeeded = s.getUnitsNeeded();
				strSecType = "Take " + unitsNeeded + " courses";
			} else if (secReqType == REQ_TYPE.Take_X_Creds) {
				int unitsNeeded = s.getUnitsNeeded();
				strSecType = "Take " + unitsNeeded + " credits";
			} else if (secReqType == REQ_TYPE.Take_Any) {
				strSecType = "Take Any";
			} else {
				strSecType = "XOR";
			}
			
			System.out.println(strSecType);

			
			for (int i = 1; i <= s.numSubsections(); i++) {
				System.out.print("Subsection " + i + ": ");
				
				REQ_TYPE reqType = s.getReqTypeBySubsection(i);
				String strType;
				if (reqType == REQ_TYPE.Take_All) {
					strType = "Take All";
				} else if (reqType == REQ_TYPE.Take_X_Courses) {
					int unitsNeeded = s.getUnitsNeededBySubsection(i);
					strType = "Take " + unitsNeeded + " courses";
				} else if (reqType == REQ_TYPE.Take_X_Creds) {
					int unitsNeeded = s.getUnitsNeededBySubsection(i);
					strType = "Take " + unitsNeeded + " credits";
				} else if (reqType == REQ_TYPE.Take_Any) {
					strType = "Take Any";
				} else {
					strType = "XOR";
				}
				
				System.out.println(strType);
				
				String[][] subSectionCourses = s.getCoursesInSubsection(i);
				System.out.println("Num series: " + subSectionCourses.length);
				for (String[] series : subSectionCourses) {
					System.out.print("Series: ");
					
					for (String course : series) {
						System.out.print(course);
						char grade = s.getGradeNeeded(course);
						boolean isHonors = s.getHonors(course);
						if (isHonors) {
							System.out.print("H");
						}
						
						System.out.print(": " + grade + ", ");
					}
					System.out.println();
				}
			}
			
			System.out.println();
			
			String[] courseList = sci.getAllCourses();
			for (String course : courseList) {
				System.out.println(course);
			}
			
			System.out.println();
			
			courseList = section.getAllCourses();
			for (String course : courseList) {
				System.out.println(course);
			}
		}
	}
	
	private class Subsection implements Serializable {
		private static final long serialVersionUID = -7230942865561478831L;

		private static final int NO_UNITS = -1;
		
		private int identifier;
		private REQ_TYPE reqType;
		/* If the reqType is Take X Credits or Take X Courses,
		   this represents X. If not, this should be -1. */
		private int unitsNeeded;
		
		private ArrayList<ReqSeries> reqSeries;
		
		public Subsection(REQ_TYPE reqType, int identifier)	{
			this(reqType, NO_UNITS, identifier);
		}
		
		public Subsection(REQ_TYPE reqType, int unitsNeeded, int identifier) {
			this.reqType = reqType;
			
			if (((reqType == REQ_TYPE.Take_X_Courses) || (reqType == REQ_TYPE.Take_X_Creds)) && (unitsNeeded > 0)) {
				this.unitsNeeded = unitsNeeded;
			} else {
				this.unitsNeeded = NO_UNITS;
			}
			
			reqSeries = new ArrayList<ReqSeries>();
		}
		
		public Subsection(Subsection original) {
			this(original.reqType, original.unitsNeeded, original.identifier);

			for (ReqSeries r : original.reqSeries) {
				this.reqSeries.add(new ReqSeries(r));
			}
		}
		
		public int getUnitsNeeded() {
			return unitsNeeded;
		}

		public boolean setUnitsNeeded(int unitsNeeded) {
			if (((reqType == REQ_TYPE.Take_X_Courses) || (reqType == REQ_TYPE.Take_X_Creds)) && (unitsNeeded > 0)) {
				this.unitsNeeded = unitsNeeded;
				return true;
			} else {
				return false;
			}
		}
		
		public REQ_TYPE getReqType() {
			return this.reqType;
		}
		
		public boolean setReqType(REQ_TYPE reqType) {
			if ((reqType == REQ_TYPE.Take_X_Courses) || (reqType == REQ_TYPE.Take_X_Creds)) {
				if (unitsNeeded > 0)	{
					this.reqType = reqType;
					return true;
				} else {
					return false;
				}
			} else {
				this.reqType = reqType;
				return true;
			}
		}
		
		public boolean setReqType(REQ_TYPE reqType, int unitsNeeded) {
			if (((reqType == REQ_TYPE.Take_X_Courses) || (reqType == REQ_TYPE.Take_X_Creds)) && (unitsNeeded > 0)) {
				this.unitsNeeded = unitsNeeded;
				return true;
			} else {
				return false;
			}
		}
		
		public int getIdentifier() {
			return identifier;
		}
		
		public int incrementIdentifier() {
			return identifier++;
		}
		
		public int decrementIdentifier() {
			return identifier--;
		}
		
		public String[][] getCourses() {
			String[][] courses = new String[reqSeries.size()][];
			
			for (int i = 0; i < courses.length; i++) {
				courses[i] = toArray(reqSeries.get(i).getCourseList());
			}

			return courses;
		}
				
		public String[] getSeriesByCourse(String courseID) {
			for (ReqSeries r : reqSeries) {
				if (r.contains(courseID)) {
					return toArray(r.getCourseList());
				}
			}
			
			return null;
		}
		
		public boolean addReqSeries(String[] courseIDs) {
			for (String course : courseIDs) {
				if (contains(course)) {
					return false;
				}
			}
			
			ReqSeries series = new ReqSeries();
			for (String course : courseIDs) {
				series.addCourse(course);
			}

			return this.reqSeries.add(series);
		}
		
		public boolean addReqSeries(String[] courseIDs, char[] gradeNeeded) {
			for (String course : courseIDs) {
				if (contains(course)) {
					return false;
				}
			}
			
			ReqSeries series = new ReqSeries();
			for (int i = 0; i < courseIDs.length; i++) {
				series.addCourse(courseIDs[i], gradeNeeded[i]);
			}
			
			return this.reqSeries.add(series);
		}		
		
		public boolean addReqSeries(String[] courseIDs, boolean[] honors) {
			for (String course : courseIDs) {
				if (contains(course)) {
					return false;
				}
			}
			
			ReqSeries series = new ReqSeries();
			for (int i = 0; i < courseIDs.length; i++) {
				series.addCourse(courseIDs[i], honors[i]);
			}
			
			return this.reqSeries.add(series);
		}

		public boolean addReqSeries(String[] courseIDs, char[] gradeNeeded, boolean[] honors) {
			for (String course : courseIDs) {
				if (contains(course)) {
					return false;
				}
			}
			
			ReqSeries series = new ReqSeries();
			for (int i = 0; i < courseIDs.length; i++) {
				series.addCourse(courseIDs[i], gradeNeeded[i], honors[i]);
			}
			
			return this.reqSeries.add(series);
		}

		public boolean removeReqSeries(String[] courseIDs) {
			for (ReqSeries s : this.reqSeries) {
				if (s.isSameSet(courseIDs)) {
					return reqSeries.remove(s);
				}
			}
			
			return false;
		}

		public boolean addCourseToSeries(String[] currentCourseIDs, String newCourseID) {
			for (ReqSeries r : reqSeries) {
				if (r.isSameSet(currentCourseIDs)) {
					return r.addCourse(newCourseID);
				}
			}
			
			return false;
		}
		
		public boolean addCourseToSeries(String[] currentCourseIDs, String newCourseID, char newGradeNeeded) {
			for (ReqSeries r : reqSeries) {
				if (r.isSameSet(currentCourseIDs)) {
					return r.addCourse(newCourseID, newGradeNeeded);
				}
			}
			
			return false;
		}
		
		public boolean addCourseToSeries(String[] currentCourseIDs, String newCourseID, boolean newHonors) {
			for (ReqSeries r : reqSeries) {
				if (r.isSameSet(currentCourseIDs)) {
					return r.addCourse(newCourseID, newHonors);
				}
			}
			
			return false;
		}
		
		public boolean addCourseToSeries(String[] currentCourseIDs, String newCourseID, char newGradeNeeded, boolean newHonors) {
			for (ReqSeries r : reqSeries) {
				if (r.isSameSet(currentCourseIDs)) {
					return r.addCourse(newCourseID, newGradeNeeded, newHonors);
				}
			}
			
			return false;
		}

		public boolean removeCourseFromSeries(String[] currentCourseIDs, String removeCourseID) {
			for (ReqSeries r : reqSeries) {
				if (r.isSameSet(currentCourseIDs)) {
					return r.removeCourse(removeCourseID);
				}
			}
			
			return false;
		}
		
		public boolean removeCourse(String removeCourseID) {
			for (ReqSeries r : reqSeries) {
				if (r.contains(removeCourseID)) {
					return r.removeCourse(removeCourseID);
				}
			}
			
			return false;
		}
		
		public char getGradeNeeded(String courseID) {
			for (ReqSeries r : reqSeries) {
				if (r.contains(courseID)) {
					return r.getGradeNeeded(courseID);
				}
			}
			
			return 'X';
		}
		
		public boolean getHonors(String courseID) {
			for (ReqSeries r : reqSeries) {
				if (r.contains(courseID)) {
					return r.getHonors(courseID);
				}
			}
			
			return false;
		}
		
		public boolean contains(String courseID) {
			for (ReqSeries r : reqSeries) {
				if (r.contains(courseID)) {
					return true;
				}
			}
			
			return false;
		}
		
		public boolean overlapsWith(Subsection other) {
			for (ReqSeries r : this.reqSeries) {
				for (ReqSeries o : other.reqSeries) {
					if (r.overlapsWith(o)) {
						return true;
					}
				}
			}
			
			return false;
		}
	}
	
	private class ReqSeries implements Serializable {
		private static final long serialVersionUID = 1759374922132972617L;
		
		private ArrayList<ReqCourse> courses;
		
		public ReqSeries()	{
			courses = new ArrayList<ReqCourse>();
		}
				
		public ReqSeries(ReqSeries other) {
			this();
			
			for (ReqCourse r : other.courses) {
				this.courses.add(new ReqCourse(r));
			}
		}
		
		public boolean contains(String courseID) {
			ReqCourse r = getCourse(courseID);
			if (r == null) {
				return false;
			} else {
				return true;
			}
		}
		
		public boolean isSameSet(String[] courseIDs) {
			if (courseIDs.length != courses.size()) {
				return false;
			}
			
			for (String course : courseIDs) {
				if (!contains(course)) {
					return false;
				}
			}
			
			return true;
		}
		
		public boolean overlapsWith(ReqSeries other) {
			for (ReqCourse r : other.courses){
				if (this.courses.contains(r)) {
					return true;
				}
			}
			
			return false;
		}
		
		public ArrayList<String> getCourseList() {
			ArrayList<String> courseList = new ArrayList<String>();
			
			for (ReqCourse r : courses) {
				courseList.add(r.courseID);
			}
			
			return courseList;
		}
		
		public char getGradeNeeded(String courseID) {
			ReqCourse r = getCourse(courseID);
			if (r == null) {
				return '1';
			}
			
			return r.getGradeNeeded();
		}
		
		public boolean setGradeNeeded(String courseID, char grade) {
			ReqCourse r = getCourse(courseID);
			if (r == null) {
				return false;
			}
			
			return r.setGradeNeeded(grade);
		}
		
		public boolean getHonors(String courseID) {
			ReqCourse r = getCourse(courseID);

			if (r == null) {
				return false;
			}
			
			return r.isHonors();
		}
		
		public boolean toggleHonors(String courseID) {
			ReqCourse r = getCourse(courseID);
			
			if (r == null) {
				return false;
			}
			
			return r.toggleHonors();
		}
		
		//returns shallow copy
		private ReqCourse getCourse(String courseID) {
			int index = courses.indexOf(new ReqCourse(courseID));
			if (index < 0) {
				return null;
			}
			
			return courses.get(index);
		}
		
		public boolean addCourse(String courseID) {
			if (courses.contains(courseID)) {
				return false;
			} else {
				return courses.add(new ReqCourse(courseID));
			}
		}
		
		public boolean addCourse(String courseID, char gradeNeeded) {
			if (courses.contains(courseID)) {
				return false;
			} else {
				return courses.add(new ReqCourse(courseID, gradeNeeded));
			}
			
		}
		
		public boolean addCourse(String courseID, boolean honors) {
			if (courses.contains(courseID)) {
				return false;
			} else {
				return courses.add(new ReqCourse(courseID, honors));
			}			
		}
		
		public boolean addCourse(String courseID, char gradeNeeded, boolean honors) {
			if (courses.contains(courseID)) {
				return false;
			} else {
				return courses.add(new ReqCourse(courseID, gradeNeeded, honors));
			}
		}
		
		public boolean removeCourse(String courseID) {
			ReqCourse r = getCourse(courseID);
			
			if (r == null) {
				return false;
			} else {
				courses.remove(r);
				return true;
			}
		}
	}
	
	private class ReqCourse implements Comparator<ReqCourse>, Serializable {
		private static final long serialVersionUID = 3993989657940886301L;
		
		public static final char DEFAULT_GRADE = 'D';
		private static final boolean DEFAULT_HONORS = false;
		private static final char HONORS = 'H';
		
		public final String courseID;
		private char gradeNeeded;
		
		/* If honors is true, the course represents both
		   the regular course, and the honors course. There
		   is no option for an honors only requirement.*/
		private boolean honors;
		
		public ReqCourse(String courseID) {
			this(courseID, DEFAULT_GRADE, DEFAULT_HONORS);
		}

		public ReqCourse(String courseID, char gradeNeeded) {
			this(courseID, gradeNeeded, DEFAULT_HONORS);
		}
		
		public ReqCourse(String courseID, boolean honors) {
			this(courseID, DEFAULT_GRADE, honors);
		}
		
		public ReqCourse(String courseID, char gradeNeeded, boolean honors) {
			this.courseID = courseID;
			this.gradeNeeded = gradeNeeded;
			this.honors = honors;
		}
		
		public ReqCourse(ReqCourse original) {
			this(original.courseID, original.gradeNeeded, original.honors);
		}
		
		public char getGradeNeeded() {
			return gradeNeeded;
		}
		
		public boolean setGradeNeeded(char grade) {
			char[] acceptedGrades = {'A', 'B', 'C', 'D', 'E', 'F', 'P', 'W', 'O'};

			if (grade != this.gradeNeeded) {
				return false;
			}
			
			for (char c : acceptedGrades) {
				if (grade == c) {
					this.gradeNeeded = grade;
					return true;
				}
			}
			
			return false;
		}
		
		public boolean isHonors() {
			return honors;
		}
		
		//Returns the new value of honors
		public boolean toggleHonors() {
			honors = !honors;
			return honors;
		}
		
		public boolean equals(Object other) {
			if (other.getClass() != this.getClass()) {
				return false;
			}
			
			return (((ReqCourse)other).courseID == this.courseID);
		}
		
		public int compare(ReqCourse course1, ReqCourse course2) {
			return course1.courseID.compareTo(course2.courseID);
		}
	}
}